<?php
require_once (dirname(__FILE__) .'/../DbAbstract.php');
require_once (dirname(__FILE__) .'/../PtDbQuery.php');
require_once (dirname(__FILE__) .'/../../ISOLang.php');
require_once (dirname(__FILE__) .'/../../PtValidate.php');
abstract class PtObjectAbstract
{
	/**
	 * List of field types
	 */
	const TYPE_INT = 1;
	const TYPE_BOOL = 2;
	const TYPE_STRING = 3;
	const TYPE_FLOAT = 4;
	const TYPE_DATE = 5;
	const TYPE_HTML = 6;
	const TYPE_NOTHING = 7;

	/**
	 * List of data to format
	 */
	const FORMAT_COMMON = 1;
	const FORMAT_LANG = 2;
	const FORMAT_SHOP = 3;

	/**
	 * List of association types
	 */
	const HAS_ONE = 1;
	const HAS_MANY = 2;

	/** @var integer Object id */
	public $id;

	/** @var string lang iso code */
	protected $lang_iso = null;

	protected static $fieldsRequiredDatabase = null;

 	/** @var array tables */
 	protected $webserviceParameters = array();

	/** @var  string path to image directory. Used for image deletion. */
	protected $image_dir = null;

	/** @var string file type of image files. Used for image deletion. */
	protected $image_format = 'jpg';

	/**
	 * @var array Contain object definition
	 * @since 1.5.0
	 */
	public static $definition = array();

	/**
	 * @var array Contain current object definition
	 */
	protected $def;

	/**
	 * @var array List of specific fields to update (all fields if null)
	 */
	protected $update_fields = null;
	
	/**
	 * @var DbAbstract An instance of the db in order to avoid calling DbAbstract::getInstance() thousands of time
	 */
	protected static $db = false;

	/**
	 * Returns object validation rules (fields validity)
	 *
	 * @param string $class Child class name for static use (optional)
	 * @return array Validation rules (fields validity)
	 */
	public static function getValidationRules($class = __CLASS__)
	{
		$object = new $class();
		return array(
			'required' => $object->fieldsRequired,
			'size' => $object->fieldsSize,
			'validate' => $object->fieldsValidate,
			'requiredLang' => $object->fieldsRequiredLang,
			'sizeLang' => $object->fieldsSizeLang,
			'validateLang' => $object->fieldsValidateLang,
		);
	}

	/**
	 * Build object
	 *
	 * @param int $id Existing object id in order to load object (optional)
	 * @param string $lang_iso Required if object is multilingual (optional)
	 */
	public function __construct($id = null, $lang_iso = null)
	{
		if (!self::$db)
			self::$db = DbAbstract::getInstance();
	
		$this->def = self::getDefinition($this);

		if ($lang_iso !== null)
			$this->lang_iso = (ISOLang::getLanguage($lang_iso) !== false) ? $lang_iso : _PT_DEFAULT_LANG_ISO_;

	 	if (!PtValidate::isTableOrIdentifier($this->def['primary']) || !PtValidate::isTableOrIdentifier($this->def['table']))
			throw new PrestaShopException('Identifier or table format not valid for class '.get_class($this));

		if ($id)
		{
			$sql = new PtDbQuery();
			$sql->from($this->def['table'], 'a');
			$sql->where('a.'.$this->def['primary'].' = '.(int)$id);

			// Get lang informations
			if ($lang_iso)
			{
				$sql->leftJoin($this->def['table'].'_lang', 'b', 'a.'.$this->def['primary'].' = b.'.$this->def['primary'].' AND b.`lang_iso` = \''.$lang_iso.'\'');
			}

			$result = self::$db->getRow($sql, true, false);
			if ($result)
			{
				$this->id = (int)$id;
				foreach ($result as $key => $value)
					if (array_key_exists($key, $this))
						$this->{$key} = $value;

				if (!$lang_iso && isset($this->def['multilang']) && $this->def['multilang'])
				{
					$sql = 'SELECT * FROM `'.pSQL(_DB_PREFIX_.$this->def['table']).'_lang`
							WHERE `'.$this->def['primary'].'` = '.(int)$id;
					$result = self::$db->executeS($sql);
					if ($result)
						foreach ($result as $row)
							foreach ($row as $key => $value)
							{
								if (array_key_exists($key, $this) && $key != $this->def['primary'])
								{
									if (!is_array($this->{$key}))
										$this->{$key} = array();
									$this->{$key}[$row['lang_iso']] = $value;
								}
							}
				}
			}
		}

		if (!is_array(self::$fieldsRequiredDatabase))
		{
			$fields = $this->getfieldsRequiredDatabase(true);
			if ($fields)
				foreach ($fields as $row)
					self::$fieldsRequiredDatabase[$row['object_name']][(int)$row['id_required_field']] = pSQL($row['field_name']);
			else
				self::$fieldsRequiredDatabase = array();
		}
	}

	/**
	 * Prepare fields for self class (add, update)
	 * All fields are verified (pSQL, intval...)
	 *
	 * @return array All object fields
	 */
	public function getFields()
	{
		$this->validateFields();
		$fields = $this->formatFields(self::FORMAT_COMMON);

		// Ensure that we get something to insert
		if (!$fields && isset($this->id) && PtValidate::isUnsignedId($this->id))
			$fields[$this->def['primary']] = $this->id;
		return $fields;
	}

	/**
	 * Prepare multilang fields
	 *
	 * @since 1.5.0
	 * @return array
	 */
	public function getFieldsLang()
	{
		// Retrocompatibility
		if (method_exists($this, 'getTranslationsFieldsChild'))
			return $this->getTranslationsFieldsChild();

		$this->validateFieldsLang();

		$fields = array();
		if ($this->lang_iso === null)
			foreach (ISOLang::getLanguages() as $language)
			{
				$fields[$language->lang_iso] = $this->formatFields(self::FORMAT_LANG, $language->lang_iso);
				$fields[$language->lang_iso]['lang_iso'] = $language->lang_iso;
			}
		else
		{
			$fields = array($this->lang_iso => $this->formatFields(self::FORMAT_LANG, $this->lang_iso));
			$fields[$this->lang_iso]['lang_iso'] = $this->lang_iso;
		}

		return $fields;
	}

	/**
	 * @since 1.5.0
	 * @param int $type FORMAT_COMMON or FORMAT_LANG
	 * @param int $lang_iso If this parameter is given, only take lang fields
	 * @return array
	 */
	protected function formatFields($type, $lang_iso = null)
	{
		$fields = array();

		// Set primary key in fields
		if (isset($this->id))
			$fields[$this->def['primary']] = $this->id;

		foreach ($this->def['fields'] as $field => $data)
		{
			// Only get fields we need for the type
			// E.g. if only lang fields are filtered, ignore fields without lang => true
			if (($type == self::FORMAT_LANG && empty($data['lang']))
				|| ($type == self::FORMAT_COMMON && (!empty($data['lang']))))
				continue;

			if (is_array($this->update_fields))
				if ((!empty($data['lang'])) && (empty($this->update_fields[$field]) || ($type == self::FORMAT_LANG && empty($this->update_fields[$field][$lang_iso]))))
					continue;

			// Get field value, if value is multilang and field is empty, use value from default lang
			$value = $this->$field;
			if ($type == self::FORMAT_LANG && $lang_iso && is_array($value))
			{
				if (!empty($value[$lang_iso]))
					$value = $value[$lang_iso];
				else if (!empty($data['required']))
					$value = $value[_PT_DEFAULT_LANG_ISO_];
				else
					$value = '';
			}

			// Format field value
			$fields[$field] = self::formatValue($value, $data['type']);
		}

		return $fields;
	}

	/**
	 * Format a data
	 *
	 * @param mixed $value
	 * @param int $type
	 */
	public static function formatValue($value, $type, $with_quotes = false)
	{
		switch ($type)
		{
			case self::TYPE_INT :
				return (int)$value;

			case self::TYPE_BOOL :
				return (int)$value;

			case self::TYPE_FLOAT :
				return (float)$value;

			case self::TYPE_DATE :
				if (!$value)
					return '0000-00-00';

				if ($with_quotes)
					return '\''.pSQL($value).'\'';
				return pSQL($value);

			case self::TYPE_HTML :
				if ($with_quotes)
					return '\''.pSQL($value, true).'\'';
				return pSQL($value, true);

			case self::TYPE_NOTHING :
				return $value;

			case self::TYPE_STRING :
			default :
				if ($with_quotes)
					return '\''.pSQL($value).'\'';
				return pSQL($value);
		}
	}

	/**
	 * Save current object to database (add or update)
	 *
	 * @param bool $null_values
	 * @param bool $autodate
	 * @return boolean Insertion result
	 */
	public function save($null_values = false, $autodate = true)
	{
		return (int)$this->id > 0 ? $this->update($null_values) : $this->add($autodate, $null_values);
	}

	/**
	 * Add current object to database
	 *
	 * @param bool $null_values
	 * @param bool $autodate
	 * @return boolean Insertion result
	 */
	public function add($autodate = true, $null_values = false)
	{
		if (!self::$db)
			self::$db = DbAbstract::getInstance();

		// Automatically fill dates
		if ($autodate && property_exists($this, 'date_add'))
			$this->date_add = date('Y-m-d H:i:s');
		if ($autodate && property_exists($this, 'date_upd'))
			$this->date_upd = date('Y-m-d H:i:s');
		
		// Database insertion
		if (isset($this->id))
			unset($this->id);
		if (!$result = self::$db->insert($this->def['table'], $this->getFields(), $null_values))
			return false;

		// Get object id in database
		$this->id = self::$db->Insert_ID();

		if (!$result)
			return false;

		// Database insertion for multilingual fields related to the object
		if (!empty($this->def['multilang']))
		{
			$fields = $this->getFieldsLang();
			if ($fields && is_array($fields))
			{
				foreach ($fields as $field)
				{
					foreach (array_keys($field) as $key)
						if (!PtValidate::isTableOrIdentifier($key))
							throw new Exception('key '.$key.' is not table or identifier, ');
					$field[$this->def['primary']] = (int)$this->id;

					$result &= self::$db->insert($this->def['table'].'_lang', $field);
				}
			}
		}

		return $result;
	}

	/**
	 * Update current object to database
	 *
	 * @param bool $null_values
	 * @return boolean Update result
	 */
	public function update($null_values = false)
	{
		if (!self::$db)
			self::$db = DbAbstract::getInstance();

		// Automatically fill dates
		if (array_key_exists('date_upd', $this))
			$this->date_upd = date('Y-m-d H:i:s');

		// Database update
		if (!$result = self::$db->update($this->def['table'], $this->getFields(), '`'.pSQL($this->def['primary']).'` = '.(int)$this->id, 0, $null_values))
			return false;

		// Database update for multilingual fields related to the object
		if (isset($this->def['multilang']) && $this->def['multilang'])
		{
			$fields = $this->getFieldsLang();
			if (is_array($fields))
			{
				foreach ($fields as $field)
				{
					foreach (array_keys($field) as $key)
						if (!PtValidate::isTableOrIdentifier($key))
							throw new Exception('key '.$key.' is not a valid table or identifier');

					$where = pSQL($this->def['primary']).' = '.(int)$this->id
								.' AND lang_iso = `'.$field['lang_iso'].'`';
					if (DbAbstract::getInstance()->getValue('SELECT COUNT(*) FROM '.pSQL(_DB_PREFIX_.$this->def['table']).'_lang WHERE '.$where))
						$result &= self::$db->update($this->def['table'].'_lang', $field, $where);
					else
						$result &= self::$db->insert($this->def['table'].'_lang', $field, $null_values);
				}
			}
		}

		return $result;
	}

	/**
	 * Delete current object from database
	 *
	 * @return boolean Deletion result
	 */
	public function delete()
	{
		if (!self::$db)
			self::$db = DbAbstract::getInstance();

		if (!$result)
			return false;

		// Database deletion for multilingual fields related to the object
		if (!empty($this->def['multilang']))
			$result &= self::$db->delete($this->def['table'].'_lang', '`'.pSQL($this->def['primary']).'` = '.(int)$this->id);

		return $result;
	}

	/**
	 * Delete several objects from database
	 *
	 * @param array $selection
	 * @return bool Deletion result
	 */
	public function deleteSelection($selection)
	{
		$result = true;
		foreach ($selection as $id)
		{
			$this->id = (int)$id;
			$result = $result && $this->delete();
		}
		return $result;
	}

	/**
	 * Toggle object status in database
	 *
	 * @return boolean Update result
	 */
	public function toggleStatus()
	{
	 	// Object must have a variable called 'active'
	 	if (!array_key_exists('active', $this))
			throw new Exception('property "active" is missing in object '.get_class($this));

	 	// Update active status on object
	 	$this->active = !(int)$this->active;

		// Change status to active/inactive
		return $this->update(false);
	}

	/**
	 * Check for fields validity before database interaction
	 *
	 * @param bool $die
	 * @param bool $error_return
	 * @return bool|string
	 */
	public function validateFields($die = true, $error_return = false)
	{
		foreach ($this->def['fields'] as $field => $data)
		{
			if (!empty($data['lang']))
				continue;

			if (is_array($this->update_fields) && empty($this->update_fields[$field]))
				continue;

			$message = $this->validateField($field, $this->$field);
			if ($message !== true)
			{
				if ($die)
					throw new Exception($message);
				return $error_return ? $message : false;
			}
		}

		return true;
	}

	/**
	 * Check for multilingual fields validity before database interaction
	 *
	 * @param bool $die
	 * @param bool $error_return
	 * @return bool|string
	 */
	public function validateFieldsLang($die = true, $error_return = false)
	{
		foreach ($this->def['fields'] as $field => $data)
		{
			if (empty($data['lang']))
				continue;

			$values = $this->$field;
			if (!is_array($values))
				$values = array($this->lang_iso => $values);

			foreach ($values as $lang_iso => $value)
			{
				if (is_array($this->update_fields) && empty($this->update_fields[$field][$lang_iso]))
					continue;

				$message = $this->validateField($field, $value, $lang_iso);
				if ($message !== true)
				{
					if ($die)
						throw new Exception($message);
					return $error_return ? $message : false;
				}
			}
		}

		return true;
	}

	/**
	 * PtValidate a single field
	 *
	 * @since 1.5.0
	 * @param string $field Field name
	 * @param mixed $value Field value
	 * @param int $lang_iso
	 * @return bool|string
	 */
	public function validateField($field, $value, $lang_iso = null)
	{
		$data = $this->def['fields'][$field];

		// Check if field is required
		$required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array();
		if (!$lang_iso || $lang_iso == _PT_DEFAULT_LANG_ID_)
			if (!empty($data['required']) || in_array($field, $required_fields))
				if (empty($value))
					return 'Property '.get_class($this).'->'.$field.' is empty';

		// Default value
		if (!$value && !empty($data['default']))
		{
			$value = $data['default'];
			$this->$field = $value;
		}

		// Check field values
		if (!empty($data['values']) && is_array($data['values']) && !in_array($value, $data['values']))
			return 'Property '.get_class($this).'->'.$field.' has bad value (allowed values are: '.implode(', ', $data['values']).')';

		// Check field size
		if (!empty($data['size']))
		{
			$size = $data['size'];
			if (!is_array($data['size']))
				$size = array('min' => 0, 'max' => $data['size']);

			$length = strlen($value);
			if ($length < $size['min'] || $length > $size['max'])
				return 'Property '.get_class($this).'->'.$field.' length ('.$length.') must be between '.$size['min'].' and '.$size['max'];
		}

		// Check field validator
		if (!empty($data['validate']))
		{
			if (!method_exists('PtValidate', $data['validate']))
				throw new Exception('Validation function not found. '.$data['validate']);

			if (!empty($value) && !call_user_func(array('PtValidate', $data['validate']), $value))
				return 'Property '.get_class($this).'->'.$field.' is not valid';
		}

		return true;
	}

	public static function displayFieldName($field, $class = __CLASS__, $htmlentities = true, Context $context = null)
	{
		global $_FIELDS;

		if (file_exists(_PS_TRANSLATIONS_DIR_.Context::getContext()->language->iso_code.'/fields.php'))
			include(_PS_TRANSLATIONS_DIR_.Context::getContext()->language->iso_code.'/fields.php');

		$key = $class.'_'.md5($field);
		return ((is_array($_FIELDS) && array_key_exists($key, $_FIELDS)) ? ($htmlentities ? htmlentities($_FIELDS[$key], ENT_QUOTES, 'utf-8') : $_FIELDS[$key]) : $field);
	}

	public function validateController($htmlentities = true)
	{
		$errors = array();
		$required_fields_database = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array();
		foreach ($this->def['fields'] as $field => $data)
		{
			// Check if field is required by user
			if (in_array($field, $required_fields_database))
				$data['required'] = true;
			
			// Checking for required fields
			if (isset($data['required']) && $data['required'] && ($value = Tools::getValue($field, $this->{$field})) == false && (string)$value != '0')
				if (!$this->id || $field != 'passwd')
					$errors[] = '<b>'.self::displayFieldName($field, get_class($this), $htmlentities).'</b> '.Tools::displayError('is required.');

			// Checking for maximum fields sizes
			if (isset($data['size']) && ($value = Tools::getValue($field, $this->{$field})) && Tools::strlen($value) > $data['size'])
				$errors[] = sprintf(
					Tools::displayError('%1$s is too long. Maximum length: %2$d'),
					self::displayFieldName($field, get_class($this), $htmlentities),
					$data['size']
				);

			// Checking for fields validity
			// Hack for postcode required for country which does not have postcodes
			if (($value = Tools::getValue($field, $this->{$field})) || ($field == 'postcode' && $value == '0'))
			{
				if (isset($data['validate']) && !PtValidate::$data['validate']($value) && (!empty($value) || $data['required']))
					$errors[] = '<b>'.self::displayFieldName($field, get_class($this), $htmlentities).'</b> '.Tools::displayError('is invalid.');
				else
				{
					if (isset($data['copy_post']) && !$data['copy_post'])
						continue;
					if ($field == 'passwd')
					{
						if ($value = Tools::getValue($field))
							$this->{$field} = Tools::encrypt($value);
					}
					else
						$this->{$field} = $value;
				}
			}
		}
		return $errors;
	}

	public function validateFieldsRequiredDatabase($htmlentities = true)
	{
		$errors = array();
		$required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array();

		foreach ($this->def['fields'] as $field => $data)
		{
			if (!in_array($field, $required_fields))
				continue;

			if (!method_exists('PtValidate', $data['validate']))
				throw new Exception('Validation function not found. '.$data['validate']);

			$value = Tools::getValue($field);

			if (empty($value))
				$errors[] = sprintf(Tools::displayError('The field %s is required.'), self::displayFieldName($field, get_class($this), $htmlentities));
		}

		return $errors;
	}

	public function getFieldsRequiredDatabase($all = false)
	{
		return DbAbstract::getInstance()->executeS('
		SELECT id_required_field, object_name, field_name
		FROM '._DB_PREFIX_.'required_field
		'.(!$all ? 'WHERE object_name = \''.pSQL(get_class($this)).'\'' : ''));
	}

	public function addFieldsRequiredDatabase($fields)
	{
		if (!is_array($fields))
			return false;

		if (!DbAbstract::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'required_field WHERE object_name = \''.get_class($this).'\''))
			return false;

		foreach ($fields as $field)
			if (!DbAbstract::getInstance()->insert('required_field', array('object_name' => get_class($this), 'field_name' => pSQL($field))))
				return false;
		return true;
	}

	/**
	 * Delete images associated with the object
	 *
	 * @return bool success
	 */
	public function deleteImage($force_delete = false)
	{
		if (!$this->id)
			return false;
		
		if ($force_delete)
		{
			/* Deleting object images and thumbnails (cache) */
			if ($this->image_dir)
			{
				if (file_exists($this->image_dir.$this->id.'.'.$this->image_format)
					&& !unlink($this->image_dir.$this->id.'.'.$this->image_format))
					return false;
			}
			if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'.'.$this->image_format)
				&& !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'.'.$this->image_format))
				return false;
			if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'.'.$this->image_format)
				&& !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'.'.$this->image_format))
				return false;
	
			$types = ImageType::getImagesTypes();
			foreach ($types as $image_type)
				if (file_exists($this->image_dir.$this->id.'-'.stripslashes($image_type['name']).'.'.$this->image_format)
				&& !unlink($this->image_dir.$this->id.'-'.stripslashes($image_type['name']).'.'.$this->image_format))
					return false;
		}
		return true;
	}

	/**
	 * Specify if an self is already in database
	 *
	 * @param int $id_entity
	 * @param string $table
	 * @return boolean
	 */
	public static function existsInDatabase($id_entity, $table)
	{
		$row = DbAbstract::getInstance()->getRow('
			SELECT `id_'.$table.'` as id
			FROM `'._PT_DB_PREFIX_.$table.'` e
			WHERE e.`id_'.$table.'` = '.(int)$id_entity
		);

		return isset($row['id']);
	}

	/**
	 * This method is allow to know if a entity is currently used
	 * @since 1.5.0.1
	 * @param string $table name of table linked to entity
	 * @param bool $has_active_column true if the table has an active column
	 * @return bool
	 */
	public static function isCurrentlyUsed($table = null, $has_active_column = false)
	{
		if ($table === null)
			$table = self::$definition['table'];

		$query = new PtDbQuery();
		$query->select('`id_'.pSQL($table).'`');
		$query->from($table);
		if ($has_active_column)
			$query->where('`active` = 1');
		return (bool)DbAbstract::getInstance()->getValue($query);
	}

	/**
	 * Fill an object with given data. Data must be an array with this syntax: array(objProperty => value, objProperty2 => value, etc.)
	 *
	 * @since 1.5.0
	 * @param array $data
	 * @param int $lang_iso
	 */
	public function hydrate(array $data, $lang_iso = null)
	{
		$this->lang_iso = $lang_iso;
		if (isset($data[$this->def['primary']]))
			$this->id = $data[$this->def['primary']];
		foreach ($data as $key => $value)
			if (array_key_exists($key, $this))
				$this->$key = $value;
	}

	/**
	 * Fill (hydrate) a list of objects in order to get a collection of these objects
	 *
	 * @since 1.5.0
	 * @param string $class Class of objects to hydrate
	 * @param array $datas List of data (multi-dimensional array)
	 * @param int $lang_iso
	 * @return array
	 */
	public static function hydrateCollection($class, array $datas, $lang_iso = null)
	{
		if (!class_exists($class))
			throw new Exception("Class '$class' not found");

		$collection = array();
		$rows = array();
		if ($datas)
		{
			$definition = self::getDefinition($class);
			if (!array_key_exists($definition['primary'], $datas[0]))
				throw new Exception("Identifier '{$definition['primary']}' not found for class '$class'");

			foreach ($datas as $row)
			{
				// Get object common properties
				$id = $row[$definition['primary']];
				if (!isset($rows[$id]))
					$rows[$id] = $row;

				// Get object lang properties
				if (isset($row['lang_iso']) && !$lang_iso)
					foreach ($definition['fields'] as $field => $data)
						if (!empty($data['lang']))
						{
							if (!is_array($rows[$id][$field]))
								$rows[$id][$field] = array();
							$rows[$id][$field][$row['lang_iso']] = $row[$field];
						}
			}
		}

		// Hydrate objects
		foreach ($rows as $row)
		{
			$obj = new $class;
			$obj->hydrate($row, $lang_iso);
			$collection[] = $obj;
		}
		return $collection;
	}

	/**
	 * Get object definition
	 *
	 * @param string $class Name of object
	 * @param string $field Name of field if we want the definition of one field only
	 * @return array
	 */
	public static function getDefinition($class, $field = null)
	{
		$reflection = new ReflectionClass($class);
		$definition = $reflection->getStaticPropertyValue('definition');
		$definition['classname'] = $class;
		if (!empty($definition['multilang']))
			$definition['associations'][Collection::LANG_ALIAS] = array(
				'type' => self::HAS_MANY,
				'field' => $definition['primary'],
				'foreign_field' => $definition['primary'],
			);

		if ($field)
			return isset($definition['fields'][$field]) ? $definition['fields'][$field] : null;
		return $definition;
	}

	/**
	 * Return the field value for the specified language if the field is multilang, else the field value.
	 *
	 * @param $field_name
	 * @param null $lang_iso
	 * @return mixed
	 * @throws PrestaShopException
	 * @since 1.5
	 */
	public function getFieldByLang($field_name, $lang_iso = null)
	{
		$definition = self::getDefinition($this);
		// Is field in definition?
		if ($definition && isset($definition['fields'][$field_name]))
		{
			$field = $definition['fields'][$field_name];
			// Is field multilang?
			if (isset($field['lang']) && $field['lang'])
			{
				if (is_array($this->{$field_name}))
					return $this->{$field_name}[$lang_iso ? $lang_iso : Context::getContext()->language->id];
			}
			return $this->{$field_name};
		}
		else
			throw new Exception('Could not load field from definition.');
	}

	/**
	 * Set a list of specific fields to update
	 * array(field1 => true, field2 => false, langfield1 => array(1 => true, 2 => false))
	 *
	 * @since 1.5.0
	 * @param array $fields
	 */
	public function setFieldsToUpdate(array $fields)
	{
		$this->update_fields = $fields;
	}
}
